package montecarlomultithread;


import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

public class MonteCarloConcurrent {

	public static double[] main(String CallPutFlag,
			double S, double X, double T, double r, double b, double v,
			int nSteps, int nSimulations) throws InterruptedException, ExecutionException {
		//final long start = System.nanoTime();

		final double value[] = MonteCarloStandardOptionAverage(CallPutFlag, S, X,
				T, r, b, v, nSteps, nSimulations);

		//final long end = System.nanoTime();

		System.out.println("Time of execution : " + value[1]);
		System.out.println("The computed value is : " + value[0]);
		
		return value;
	}
	
	
	

	public static double[] MonteCarloStandardOptionAverage (final String CallPutFlag,
			final double S, final double X, final double T, final double r, final double b, final double v,
			final int nSteps, final int nSimulations) throws InterruptedException, ExecutionException {
		
		double finish[] = new double[2];
		double result = 0; 

		final int numberOfCores = Runtime.getRuntime().availableProcessors();
		final double blockingCoefficient = 0.9;
		final int poolSize = (int) (numberOfCores / (1 - blockingCoefficient));

		System.out.println("Number of Cores available is " + numberOfCores);
		System.out.println("Pool size is " + poolSize);


		final List<Callable<Double>> partitions = new ArrayList<Callable<Double>>();

		
		/************************ Clock Start **********************/
		final long start = System.nanoTime();
		
		for (int i = 1; i < nSimulations; i++) {
		
			

			partitions.add(new Callable<Double>() {
				public Double call() throws Exception {
					return MonteCarloStandardOption(CallPutFlag, S, X, T, r, b, v, nSteps, nSimulations);
				}
			});

		}
		
		final ExecutorService executorPool = Executors.newFixedThreadPool(poolSize); 
			    
		final List<Future<Double>> valueOfOptions = executorPool.invokeAll(partitions, 10000, TimeUnit.SECONDS);
			      
		
		for(final Future<Double> valueOfAnOption : valueOfOptions)  
			result += valueOfAnOption.get(); 

		executorPool.shutdown();
		finish[0] = result/nSimulations;  // Put the total in finish 0
		
		final long end = System.nanoTime();
		/************************ Clock End ************************/
		
		finish[1] = (end - start) / 1.0e9; // Put the time in finish 1
		
		System.out.println("finish 0 : " + finish[0] + " // finish 1 : " + finish[1]);
			    

		return finish;

	}



	public static double MonteCarloStandardOption (String CallPutFlag,
			double S, double X, double T, double r, double b, double v,
			int nSteps, int nSimulations) {
	
	
		/*System.out.println("At the begining : CallPut = " + CallPutFlag);
		System.out.println("At the begining : S = " + S);
		System.out.println("At the begining : X = " + X);
		System.out.println("At the begining : T = " + T);
		System.out.println("At the begining : r = " + r);
		System.out.println("At the begining : b = " + b);
		System.out.println("At the begining : v = " + v);
		System.out.println("At the begining : nsteps = " + nSteps);*/
		double total = 0;
		double dt;
		double St = 0;
		double Sum = 0;
		double Drift;
		double vSqrdt;
		int k, z = 0;
		Random random = new Random();
	
	
		dt = T / nSteps;
		Drift = (b - Math.pow(v, 2) / 2) * dt;
		vSqrdt = v * Math.sqrt(dt);
	
		if (CallPutFlag == "Call") {
			z = 1;
		} else if (CallPutFlag == "Put") {
			z = -1;
		}
	
		
	
		St = S;
	
		for (k = 1; k < nSteps; k++) {
			St = St * Math.exp(Drift + vSqrdt * random.nextGaussian());
		}
	
		Sum = Sum + Math.max(z * (St - X), 0);
	
	
		total = Math.exp(-r * T) * (Sum);
	
		return total;
	
	}

}


